#include <stdio.h>
#include <string.h>
#include <ctype.h>
#include <stdlib.h>
#include <stdarg.h>
#include <time.h>
#include <math.h>
#include <stdint.h>
#include <rtthread.h>
#include "sys.h"
#include "tools.h"
#include "rtc.h"
#include "sys_timer.h"
#include "onps.h"
#define SYMBOL_GLOBALS
#include "tcp_client.h"
#undef SYMBOL_GLOBALS

BOOL connect_tcp_server(INT nThIdx, SOCKET hSocket, const CHAR *pszSrvIP, USHORT usSrvPort, INT nRcvTimeout, INT nConnTimeout)
{
  EN_ONPSERR enErr; 
    
  if(!connect(hSocket, pszSrvIP, usSrvPort, nConnTimeout))
  {
    if(!socket_set_rcv_timeout(hSocket, nRcvTimeout, &enErr))
    {
      os_thread_mutex_lock(o_hMtxPrintf);
      printf("socket_set_rcv_timeout() failed, %s\r\n", onps_error(enErr));
      os_thread_mutex_unlock(o_hMtxPrintf);
    }
    
    os_thread_mutex_lock(o_hMtxPrintf);
    printf("#%d#>connect %s:%d successfully!\r\n", nThIdx, pszSrvIP, usSrvPort);
    os_thread_mutex_unlock(o_hMtxPrintf);
    
    return TRUE; 
  }
  else
  {
    os_thread_mutex_lock(o_hMtxPrintf);
    printf("#%d#>connect %s:%d failed, %s\r\n", nThIdx, pszSrvIP, usSrvPort, onps_get_last_error(hSocket, NULL));
    os_thread_mutex_unlock(o_hMtxPrintf); 
    
    return FALSE; 
  }
}

void handle_read(INT nThIdx, SOCKET hSocket, UCHAR *pubRcvBuf, CHAR *pbParsingState, UINT *punWriteIdx, UINT *punReadIdx, UINT *punPktStartIdx, UINT *punTimestampToAck)
{
  UINT unRemainBytes = RCV_BUF_SIZE - *punWriteIdx; 
  INT nRcvBytes = recv(hSocket, pubRcvBuf + *punWriteIdx, unRemainBytes); 
  if (nRcvBytes > 0)
  {
    PST_COMMUPKT_HDR pstHdr; 
    *punWriteIdx += (UINT)nRcvBytes; 
    for (; *punReadIdx < *punWriteIdx; )
    {
      UCHAR ch = pubRcvBuf[*punReadIdx]; 
      switch(*pbParsingState)
      {
        case 0: //* 找到头部标志
          if (ch == PKT_FLAG)
          {
            *punPktStartIdx = *punReadIdx;
            *pbParsingState = 1;
          }
          else
          {
            *punReadIdx++;
            break;
          }
          
        case 1: //* 获取整个头部数据 
          if (*punWriteIdx - *punPktStartIdx < sizeof(ST_COMMUPKT_HDR))
            return; 
          else
          {
            pstHdr = (PST_COMMUPKT_HDR)(pubRcvBuf + *punPktStartIdx);
            if (pstHdr->usDataLen < PKT_DATA_LEN_MAX) //* 只有小于系统允许的最大数据长度才是合法报文
              *pbParsingState = 2;
            else
            {                 
              //* 不合法，重新查找
              *punReadIdx = *punPktStartIdx + 1; 
              *pbParsingState = 0;
              break; 
            }
          }
          
        case 2: //* 获取完整报文
          pstHdr = (PST_COMMUPKT_HDR)(pubRcvBuf + *punPktStartIdx);
          if (*punWriteIdx - *punPktStartIdx < sizeof(ST_COMMUPKT_HDR) + (UINT)pstHdr->usDataLen + 1)
            return; 
          else
          {
            *punReadIdx = *punPktStartIdx + sizeof(ST_COMMUPKT_HDR) + (UINT)pstHdr->usDataLen;

            //* 尾部标识必须匹配
            ch = pubRcvBuf[*punReadIdx];
            if (ch == PKT_FLAG)
            {
              //* 判断校验和是否正确
              USHORT usPktChecksum = pstHdr->usChechsum;
              pstHdr->usChechsum = 0;
              USHORT usChecksum = crc16(pubRcvBuf + *punPktStartIdx + sizeof(CHAR), sizeof(ST_COMMUPKT_HDR) - sizeof(CHAR) + (UINT)pstHdr->usDataLen, 0xFFFF); 
              if (usChecksum == usPktChecksum)
              {               
                CHAR szPktTime[20]; 
                UnixTimeToLocalTime((time_t)pstHdr->unTimestamp + 8 * 3600, szPktTime); 

                //* 收到对端下发的模拟控制指令
                if (pstHdr->bCmd == 1)
                {
                  UCHAR ubaSndBuf[sizeof(ST_COMMUPKT_ACK)];
                  PST_COMMUPKT_ACK pstAck = (PST_COMMUPKT_ACK)ubaSndBuf;
                  pstAck->stHdr.bFlag = (CHAR)PKT_FLAG;
                  pstAck->stHdr.bCmd = 0x01; 
                  pstAck->stHdr.bLinkIdx = (CHAR)nThIdx;
                  pstAck->stHdr.unTimestamp = (UINT)Time(); 
                  pstAck->stHdr.usDataLen = sizeof(UINT) + sizeof(CHAR); 
                  pstAck->stHdr.usChechsum = 0; 
                  pstAck->unTimestamp = pstHdr->unTimestamp; 
                  pstAck->bLinkIdx = pstHdr->bLinkIdx; 
                  pstAck->bTail = (CHAR)PKT_FLAG; 
                  pstAck->stHdr.usChechsum = crc16(&ubaSndBuf[sizeof(CHAR)], sizeof(ST_COMMUPKT_ACK) - 2 * sizeof(CHAR), 0xFFFF);
                  
                #if 1
                  os_thread_mutex_lock(o_hMtxPrintf);
                  printf("%d#%s#>recved the control cmd packet, cmd = 0x%02X, LinkIdx = %d, data length is %d bytes, %d\r\n", nThIdx, szPktTime, pstHdr->bCmd, pstHdr->bLinkIdx, pstHdr->usDataLen, nRcvBytes);
                  os_thread_mutex_unlock(o_hMtxPrintf);
                #endif                  
                  
                  send(hSocket, (UCHAR *)ubaSndBuf, sizeof(ST_COMMUPKT_ACK), 3);                  
                }
                else if (pstHdr->bCmd == 0)
                {
                  PST_COMMUPKT_ACK pstAck = (PST_COMMUPKT_ACK)pstHdr;
                  if (pstAck->unTimestamp == *punTimestampToAck && pstAck->bLinkIdx == (CHAR)nThIdx)
                  {
                    *punTimestampToAck = 0;
                    
                  #if 0                    
                    os_thread_mutex_lock(o_hMtxPrintf);
                    printf("%d#%s#>recved acknowledge packet, AckedLinkIdx = %d, AckedTimestamp <", nThIdx, szPktTime, pstAck->bLinkIdx);
                    UnixTimeToLocalTime((time_t)pstAck->unTimestamp, (INT8S *)szPktTime);
                    printf("%s>\r\n", szPktTime);
                    os_thread_mutex_unlock(o_hMtxPrintf);
                  #endif
                  }
                }
                else; 
                
                //* 搬运剩余的字节
                UINT unRemainBytes = *punWriteIdx - *punReadIdx - 1; 
                if (*punReadIdx < *punWriteIdx)
                  memmove(pubRcvBuf, pubRcvBuf + *punReadIdx + 1, unRemainBytes);
                *punWriteIdx = unRemainBytes;

                //* 开始截取下一个报文
                *punReadIdx = 0;
              }
              else
              {
                //* 不合法，从第一个标识后的字符开始重新查找
                *punReadIdx = *punPktStartIdx + 1;
              }
            }
            else
            {
              //* 不合法，从第一个标识后的字符开始重新查找
              *punReadIdx = *punPktStartIdx + 1;
            }                    
          }

          *pbParsingState = 0;
          break; 
      }
    }
  }
}

void tcp_commu_packet_ready(INT nThIdx, UCHAR *pubSndBuf, USHORT usDataLen, UINT unSeqNum, UINT *punTimestampToAck)
{
  PST_COMMUPKT_HDR pstHdr = (PST_COMMUPKT_HDR)pubSndBuf;
  pstHdr->bFlag = (CHAR)PKT_FLAG; 
  pstHdr->bCmd = 0x00; 
  pstHdr->bLinkIdx = (CHAR)nThIdx; 
  pstHdr->unSeqNum = unSeqNum; 
  pstHdr->unTimestamp = Time(); 
  pstHdr->usDataLen = usDataLen; 
  pstHdr->usChechsum = 0; 
  pstHdr->usChechsum = crc16(pubSndBuf + sizeof(CHAR), sizeof(ST_COMMUPKT_HDR) - sizeof(CHAR) + usDataLen, 0xFFFF); 
  pubSndBuf[sizeof(ST_COMMUPKT_HDR) + usDataLen] = PKT_FLAG; 
  
  //* 更新要应答报文标识
  *punTimestampToAck = pstHdr->unTimestamp; 
}

BOOL tcp_commu_packet_send(INT nThIdx, SOCKET hSocket, UCHAR *pubSndPacket)
{
  PST_COMMUPKT_HDR pstHdr = (PST_COMMUPKT_HDR)pubSndPacket;
  
  //* 日志输出
  CHAR szPktTime[20]; 
  UnixTimeToLocalTime((time_t)pstHdr->unTimestamp, szPktTime); 
#if 0  
  os_thread_mutex_lock(o_hMtxPrintf);
  printf("%d#%s#>sent user's data to peer, the cmd is 0x%02X, the data length is %d bytes\r\n", nThIdx, szPktTime, pstHdr->bCmd, pstHdr->usDataLen);   
  os_thread_mutex_unlock(o_hMtxPrintf);
#endif  
    
  INT nPacketLen = sizeof(ST_COMMUPKT_HDR) + pstHdr->usDataLen + sizeof(CHAR); 
  INT nSndBytes, nSndNum = 0; 
  
__lblSend: 
  if(nSndNum > 2)         
    return FALSE;  
  
  //* 发送数据 
  UINT unStartMillSecs = GetElapsedMSecs();   
  nSndBytes = send(hSocket, (UCHAR *)pubSndPacket, nPacketLen, 3); 
  if(nSndBytes == nPacketLen)      
    return TRUE; 
  else
  {
    EN_ONPSERR enErr; 
    const CHAR *pszErr = onps_get_last_error(hSocket, &enErr); 
    
    os_thread_mutex_lock(o_hMtxPrintf);    
    printf("<err>%d#%s#>sent user's data to peer (%d), the cmd is 0x%02X, the data length is %d bytes, %04X, wait %d millsecs\r\n", nThIdx, szPktTime, pstHdr->unSeqNum, pstHdr->bCmd, pstHdr->usDataLen, pstHdr->usChechsum, GetElapsedMSecs() - unStartMillSecs); 
    printf("<err>%d#%s#>sent %d bytes failed (try num is %d), %s, socket: %d\r\n", nThIdx, szPktTime, nPacketLen, nSndNum + 1, pszErr, hSocket);  
    os_thread_mutex_unlock(o_hMtxPrintf); 
    
    if(enErr != ERRTCPACKTIMEOUT)
      return FALSE; 
    
    nSndNum++; 
    goto __lblSend; 
  }
}
